﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using System.Reflection;
using FinPlusDBConn;
using FinPlusCommon;
using ExcelDna.ComInterop;
using u = FinPlusUtility.Utility;
using log = Logger.Logger;

namespace FinPlusRTDQuery
{
    [Guid("62af0a90-8aff-49df-8aec-19e2f47dc406"), ProgId("rtquery")]
    public class FinPlusRTDQuery : IRtdServer
    {
        private IRTDUpdateEvent _callback;
        private static IDictionary<int, IFinPlusQuery> _querys;
        private static IFinPlusDBConn _conn;
        private static u.Config _config;
        private static u.Env _env = u.Env.unknown;
        private static Object _lock = new Object();

        //public
        [ExcelFunction(Description = "assembly info", Category = "FinPlus.Assembly")]
        public static object FinPlusQryAssembly()
        {
            return u.AssemblyInfo(Assembly.GetExecutingAssembly());
        }

        [ExcelFunction(Description = "select where once or continous", Category = "FinPlus.Query")]
        public static object FinPlusQry(object QueryType, object Group, object Date, object Where, object ConType, object DisplayFields, object DisplayOrder)
        {
            try
            {
                object res = XlCall.Excel(XlCall.xlfRtd, "rtquery", null, QueryType, Group, Date, Where, ConType, DisplayFields, DisplayOrder);
                return u.StringToArray2d(res.ToString());
            }
            catch (QueryException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                return e;
            }
        }

        public void AutoClose()
        {

        }

        public void AutoOpen()
        {
            ComServer.DllRegisterServer();
        }

        public object ConnectData(int topicId, ref Array strings, ref bool newValues)
        {
            try
            {
                if (strings.Length != 7) return "#seven parameters are required";

                QueryType queryType;
                if (!Enum.TryParse(strings.GetValue(0).ToString(), true, out queryType)) return "#Query type should be Select, SelectAsOf or SelectFrom";

                DateTime dt = DateTime.MinValue;
                DateTime.TryParse(strings.GetValue(2).ToString(), out dt);

                ConType conType;
                if (!Enum.TryParse(strings.GetValue(4).ToString(), true, out conType)) return "#ConnectionType type should None, All, New, Amend";

                _querys[topicId] = _conn.Query(queryType, strings.GetValue(1).ToString(), dt, strings.GetValue(3).ToString(), conType, strings.GetValue(5).ToString(), strings.GetValue(6).ToString());
                //log.Log(Logger.LoggingLevel.Debug, "FinPlusQry ConnectData " + _querys[topicId].QueryString);            

                _callback.UpdateNotify();
                
                return GetFunctions(topicId);
            }
            catch (QueryException e)
            {
                return e.ToString();
            }
        }

        public void DisconnectData(int topicId)
        {
            try
            {
                if (!_querys.ContainsKey(topicId)) return;
                _conn.DisposeQuery(_querys[topicId]);
                _querys.Remove(topicId);
            }
            catch (QueryException e) 
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        public int Heartbeat()
        {
            return 1;
        }

        public Array RefreshData(ref int topicCount)
        {
            object[,] data = new object[2, _querys.Count];

            try
            {
                int index = 0;

                foreach (int topicId in _querys.Keys)
                {
                    data[0, index] = topicId;
                    data[1, index] = GetFunctions(topicId);
                    ++index;
                }

                topicCount = _querys.Count;
            }
            catch(Exception e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }

            return data;
        }

        public int ServerStart(IRTDUpdateEvent callback)
        {
            try
            {
                _callback = callback;
                _querys = new Dictionary<int, IFinPlusQuery>();
                Connect();
            }
            catch (QueryException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                return 0;
            }

            return 1;
        }

        public void ServerTerminate()
        {
            try
            {
                if (_conn != null)
                {
                    _conn.Dispose();
                    _conn = null;
                }
            }
            catch (QueryException e) 
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }

            //log.Log(Logger.LoggingLevel.Debug, "FinPlusQry ServerTerminate");
        }

        //private
        private static object GetFunctions(int topic)
        {
            try
            {
                if (!_querys.ContainsKey(topic)) return "Waiting!";
                return _querys[topic].Functions.ToString(FieldType.Value);
            }
            catch (QueryException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
                return e.ToString();
            }
        }

        private void Connect()
        {
            try
            {
                //load config and initialize logger
                if (_conn != null) return;

                lock (_lock)
                {
                    if (_conn != null) return;

                    var p = u.GetConfigPath(Assembly.GetExecutingAssembly());
                    _config = u.ConfigXml(p, out _env);
                    log.Initialise(p, _env.ToString());

                    _conn = new FinPlusDBConn.FinPlusConn();
                    _conn.QueryUpdate += Callback;
                }
            }
            catch (QueryException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }

        //events
        private void Callback(object s, EventArgs a)
        {
            try
            {
                _callback.UpdateNotify();
            }
            catch (QueryException e)
            {
                log.Log(Logger.Level.Error, e.Message, e);
            }
        }
    }
}